﻿Module modConvert
    Friend Sub CreateXMLFromWorld(ByVal Input As String, ByVal Output As String, ByVal ForceOverwrite As Boolean)
        Dim InputDir As New System.IO.DirectoryInfo(Input)
        Dim OutputDir As New System.IO.DirectoryInfo(Output)

        If Not System.IO.File.Exists(String.Format("{0}\{1}", InputDir.FullName, "level.dat")) Then
            Throw New ApplicationException(String.Format("Directory '{0}' does not a Minecraft world.", InputDir.FullName))
        End If

        If Not ForceOverwrite AndAlso OutputDir.Exists Then
            Throw New ApplicationException(String.Format("Directory '{0}' already exists. Use /y parameter to force overwrite.", OutputDir.FullName))
        End If


        NBTCompressedToXML(InputDir, OutputDir, "level.dat")


        Dim PlayersDir As New System.IO.DirectoryInfo(String.Format("{0}\{1}", InputDir.FullName, "players"))

        If PlayersDir.GetFiles("*.dat").Count > 0 AndAlso Not System.IO.Directory.Exists(String.Format("{0}\{1}", OutputDir.FullName, "players")) Then
            System.IO.Directory.CreateDirectory(String.Format("{0}\{1}", OutputDir.FullName, "players"))
        End If

        For Each f As System.IO.FileInfo In PlayersDir.GetFiles("*.dat")
            NBTCompressedToXML(InputDir, OutputDir, String.Format("players\{0}", f.Name))
        Next


        Dim DataDir As New System.IO.DirectoryInfo(String.Format("{0}\{1}", InputDir.FullName, "data"))

        If DataDir.GetFiles("*.dat").Count > 0 AndAlso Not System.IO.Directory.Exists(String.Format("{0}\{1}", OutputDir.FullName, "data")) Then
            System.IO.Directory.CreateDirectory(String.Format("{0}\{1}", OutputDir.FullName, "data"))
        End If

        For Each f As System.IO.FileInfo In DataDir.GetFiles("*.dat")
            If f.Name = "idcounts.dat" Then
                NBTUncompressedToXML(InputDir, OutputDir, String.Format("data\{0}", f.Name))
            Else
                NBTCompressedToXML(InputDir, OutputDir, String.Format("data\{0}", f.Name))
            End If
        Next

        Dim MapDirs As New List(Of String)
        MapDirs.Add("DIM1")
        MapDirs.Add("DIM-1")
        MapDirs.Add("region")

        For Each MapDirName As String In MapDirs
            Dim MapDir As New System.IO.DirectoryInfo(String.Format("{0}\{1}", InputDir.FullName, MapDirName))

            If MapDir.GetFiles("*.mca").Count > 0 AndAlso Not System.IO.Directory.Exists(String.Format("{0}\{1}", OutputDir.FullName, MapDirName)) Then
                System.IO.Directory.CreateDirectory(String.Format("{0}\{1}", OutputDir.FullName, MapDirName))
            End If

            For Each f As System.IO.FileInfo In MapDir.GetFiles("*.mca")
                MCAToXML(InputDir, OutputDir, String.Format("{0}\{1}", MapDirName, f.Name))
            Next
        Next


    End Sub

    Private Sub NBTStreamToXML(ByVal InStr As System.IO.Stream, ByVal OutFilename As String)
        Dim x As tagCompound = Nothing

        Dim b As Byte = ReadBytesFromStream(InStr, 1)(0)
        Select Case b
            Case 10
                x = New tagCompound(InStr)
            Case Else
                Throw New ApplicationException(String.Format("Tag-ID '{0}' is not valid at start of file.", b))
        End Select

        If Not System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(OutFilename)) Then
            System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(OutFilename))
        End If

        Using Out As New System.IO.FileStream(OutFilename, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.None)
            Dim Serializer As New System.Xml.Serialization.XmlSerializer(x.GetType)
            Serializer.Serialize(Out, x)
        End Using

    End Sub

    Private Sub NBTUnCompressedToXML(ByVal InDir As System.IO.DirectoryInfo, ByVal OutDir As System.IO.DirectoryInfo, ByVal Filename As String)
        Using fs As New System.IO.FileStream(String.Format("{0}\{1}", InDir.FullName, Filename), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)
            Console.WriteLine("Converting file {0}\{1}...", InDir.FullName, Filename)
            NBTStreamToXML(fs, String.Format("{0}\{1}", OutDir.FullName, String.Format("{0}.xml", Filename)))
        End Using
    End Sub

    Private Sub NBTCompressedToXML(ByVal InDir As System.IO.DirectoryInfo, ByVal OutDir As System.IO.DirectoryInfo, ByVal Filename As String)
        Using fs As New System.IO.FileStream(String.Format("{0}\{1}", InDir.FullName, Filename), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)
            Using gs As New System.IO.Compression.GZipStream(fs, IO.Compression.CompressionMode.Decompress)
                Console.WriteLine("Converting file {0}\{1}...", InDir.FullName, Filename)
                NBTStreamToXML(gs, String.Format("{0}\{1}", OutDir.FullName, String.Format("{0}.xml", Filename)))
            End Using
        End Using
    End Sub

    Private Sub MCAToXML(ByVal InDir As System.IO.DirectoryInfo, ByVal OutDir As System.IO.DirectoryInfo, ByVal Filename As String)
        Using fs As New System.IO.FileStream(String.Format("{0}\{1}", InDir.FullName, Filename), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)

            Console.WriteLine("Converting file {0}\{1}...", InDir.FullName, Filename)

            Dim lHeader As New List(Of MCAHeader)

            For i As Integer = 0 To 1023
                Dim Offset(3) As Byte
                fs.Read(Offset, 1, 3)

                Dim h As New MCAHeader
                h.Offset = BitConverter.ToUInt32(Offset.Reverse.ToArray, 0)
                h.Sectorcount = ReadBytesFromStream(fs, 1)(0)

                lHeader.Add(h)
            Next

            For i As Integer = 0 To 1023
                lHeader(i).Timestamp = BitConverter.ToUInt32(ReadBytesFromStream(fs, 4).Reverse.ToArray, 0)
            Next

            For Each h As MCAHeader In lHeader
                If h.Offset <> 0 Then

                    Console.WriteLine("Converting chunk {0}\{1}...", Filename, lHeader.IndexOf(h))

                    fs.Position = h.Offset * 4096
                    Dim Outfilename As String = String.Format("{0}\{1}\{2}.xml", OutDir.FullName, Filename, lHeader.IndexOf(h))
                    If Not System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(Outfilename)) Then
                        System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Outfilename))
                    End If

                    Using ms As New System.IO.MemoryStream(ReadBytesFromStream(fs, h.Sectorcount * 4096))
                        ChunkToXML(ms, String.Format("{0}\{1}\{2}.xml", OutDir.FullName, Filename, lHeader.IndexOf(h)))
                    End Using
                End If
            Next

        End Using
    End Sub

    Private Sub ChunkToXML(ByVal InStr As System.IO.Stream, ByVal OutFilename As String)
        Dim ChunkLength As Int32
        Dim bChunkLength() As Byte = ReadBytesFromStream(InStr, 4)
        ChunkLength = BitConverter.ToInt32(bChunkLength.Reverse.ToArray, 0)

        Dim Compressiontype As Byte = ReadBytesFromStream(InStr, 1)(0)

        Using ms As New System.IO.MemoryStream(ReadBytesFromStream(InStr, ChunkLength - 1))
            Using zs As New zlib.ZInputStream(ms)
                Dim ms2 As New System.IO.MemoryStream()
                Dim b(1023) As Byte
                Dim Read As Integer = 0
                Do
                    Read = zs.Read(b, 0, 1024)
                    ms2.Write(b, 0, 1024)

                Loop Until Read < 1024
                ms2.Position = 0

                NBTStreamToXML(ms2, OutFilename)

            End Using
        End Using

    End Sub

    Friend Sub CreateWorldFromXML(ByVal Input As String, ByVal Output As String, ByVal ForceOverwrite As Boolean)
        Dim InputDir As New System.IO.DirectoryInfo(Input)
        Dim OutputDir As New System.IO.DirectoryInfo(Output)

        If Not System.IO.File.Exists(String.Format("{0}\{1}", InputDir.FullName, "level.dat.xml")) Then
            Throw New ApplicationException(String.Format("Directory '{0}' does not an XML-converted Minecraft world.", InputDir.FullName))
        End If

        If Not ForceOverwrite AndAlso OutputDir.Exists Then
            Throw New ApplicationException(String.Format("Directory '{0}' already exists. Use /y parameter to force overwrite.", OutputDir.FullName))
        End If

        Throw New ApplicationException("This feature is not yet implemented.")
    End Sub

    Friend Function ReadBytesFromStream(ByVal Str As System.IO.Stream, ByVal Count As Int32) As Byte()
        Dim b(Count - 1) As Byte
        If Str.Read(b, 0, Count) = 0 Then

        End If

        Return b
    End Function



End Module

